//Nicholas Lineback and Jeremy Cowan

#include "hierarchy.h"
//==========================================================
// hierarchy()
// OVERVIEW: constuctor
//==========================================================
hierarchy::hierarchy()
{
  hitTimes[0] = gHitTime;
  hitTimes[1] = gL2Time;
  hitTimes[2] = gL3Time;

  cache L1(gNumBlocks);
  mem_system.push_back(L1);
  if(gL2Size != 0)
  {
    cache L2(gL2Size);
    mem_system.push_back(L2);
  }
  else 
    mem_system[0].setEnd(true);

  if(gL3Size != 0)
  {
    cache L3(gL3Size);
    L3.setEnd(true);
    mem_system.push_back(L3);
  }
  else
    mem_system[1].setEnd(true);
}

//==========================================================
// load_store(inst)
// OVERVIEW: performs instruction inst
// MODIFIES: this
//==========================================================
void hierarchy::load_store(mem_instruction inst)
{
  int lastMiss = 0;

  if(inst.read_write == 0)
  { 
    for( int i = 0; i < mem_system.size();i++)
    {
      if(mem_system[i].read(inst.address))
      {
	if(gVerbose)
	{
	  std::cout << std::setw(8) << std::setfill('0') 
		    << std::hex << inst.address 
		    << " Hit on L" << i+1 << std::endl;
	}
	break;
      }
      else
      {
	lastMiss = i;
	if(gVerbose)
	{
	  std::cout << std::setw(8) << std::setfill('0') 
		  << std::hex << inst.address 
		  << " Miss on L" << i+1 << std::endl;
	}
      }
    }
    for(int i = lastMiss; i >=0; i--)
    {
      mem_system[i].write_up(inst.address);
    }
  }
  else
  {
    for(int i = 0; i < mem_system.size(); i++)
    {
      mem_system[i].write(inst.address);
    }
  }
}

//==========================================================
// updateLRU()
// OVERVIEW: updates LRU for all levels of cache
// MODIFIES: this.mem_system
//==========================================================
void hierarchy::updateLRU()
{
  for(int i = 0; i < mem_system.size();i++)
  {
    mem_system[i].updateLRU();
  }
}

//==========================================================
// calc_AMAT()
// OVERVIEW: calculates the AMAT
// RETURNS: double AMAT
//==========================================================
double hierarchy::calc_AMAT()
{
  double AMAT = gMainMemTime;

  for( int i = mem_system.size() -1; i >= 0; i--)
  {
    AMAT = hitTimes[i] + AMAT/mem_system[i].getAccesses()*
      (mem_system[i].getAccesses() - mem_system[i].getHits());
  }
  return AMAT;
}

//==========================================================
// printData()
// OVERVIEW: prints data for all levels of cache
//==========================================================
void hierarchy::printData()
{
  for(int i = 0; i < mem_system.size(); i++)
  {
    std::cout << std::dec << "L" << i + 1 << " hits = "
	      << mem_system[i].getHits() << std::endl;
    std::cout << "L" << i + 1 << " accesses = "
	      << mem_system[i].getAccesses() << std::endl;
    std::cout << "L" << i + 1 << " hit rate = "
	      << (100*mem_system[i].getHits()) / (double)(mem_system[i].getAccesses())
	      << "\%" << std::endl;
  }
}

hierarchy::~hierarchy()
{
}
